Zurück in Elementare ProgrammentwicklungWeiter in Elementare ProgrammentwicklungBeispiel Gewinnverteilung Zusammenfassung ?

Im Mittelpunkt dieses Beispiels steht die Gewinnverteilung einer Aktiengesellschaft (AG) nach Artikel 671 des schweizerischen Obligationenrechts (OR). Es soll nicht nur die Themen Objekte und Benutzerschnittstelle vertiefen, sondern auch die phasenweise Entwicklung eines komplexeren Programms veranschaulichen. Das Thema ist wie folgt gegliedert:

  1. Buchhalterische Voraussetzungen
  2. Spezifikation

  3. Entwurf
  4. Implementation


Erfolgsverteilung in der doppelten Buchhaltung

Der Ablauf einer doppelten Buchhaltung besteht aus drei Phasen:

  1. Zu Beginn der Buchungsperiode wird die Bilanz (Vermögensrechnung) eröffnet.

  2. Während der Buchungsperiode wird der Geschäftsverkehr doppelt verbucht. Jeder Geschäftsfall wir durch einen oder mehrere Buchungssätze festgehalten. Doppelt verbuchen heisst den Sollspalten eines Buchungssatzes gleich viel belasten wie seinen Habenspalten gutgeschrieben wird.

  3. Am Ende der Buchungsperiode werden die Vermögens- und Schuldkonten in der Bilanz zusammengefasst. Die Aufwand- und Erfolgskonten werden in der Erfolgsrechnung (Gewinn- und Verlustrechnung) abgeschlossen.

Zu den Abschlussarbeiten der Erfolgsrechnung gehört auch die Verbuchung der Gewinnverteilung. Wir veranschaulichen an ihrer Implementation ausgewählte Begriffe der Programmentwicklung unter VBA und Excel. Die Buchhaltung einer Aktiengesellschaft (AG) enthält die folgenden Erfolgsverteilungskonten:

Bei der Präsentation des Beispiels folgen wir den Entwicklungsphasen Spezifikation, Entwurf und Implementation.


Spezifikation

a) Grobspezifikation

Der folgende Bildschirmausschnitt beschreibt den Zweck der Arbeitsmappe Gewinnverteilung.xls. Der Entwickler trägt sie zu Beginn des Projekts unter dem Excel-Menüpunkt »Datei/Eigenschaften ein:

b) Feinspezifikation

Die folgende Feinspezifikation geht nach dem EVA-Prinzip des Themas Modularisierung vor und wendet die gesetzlichen Vorschriften von OR 671 und die kaufmännische Praxis auf eine konkrete Gewinnverteilungsaufgabe an:

Eingabe

Weil das Programm - anders als ein reales Buchhaltungspaket - den Verkehr der vergangenen Buchungsperiode noch nicht aufgezeichnet hat, muss es den zusammengefassten Verkehr vom Benutzer einlesen, zum Beispiel ...

Beteiligte Konten

Beispielsaldi

Aufwand (Unternehmungsaufwand)

150’000.-

Ertrag (Unternehmungsertrag)

180’000.-

Aktienkapital (Grundkapital)

100’000.-

Reserve (Reservefonds)

5’000.-

Tantième in %

10%

Ausgabe

Alle Buchungssätze der Gewinnverteilung sollen im Format “<Sollkonto> an <Habenkonto>, <Betrag>” auf dem Tabellenblatt ausgegeben werden:

Verarbeitung

a. Wortlaut und Interpretation des Gesetzes

OR 671 regelt die Erfolgsverteilung einer Aktiengesellschaft. Die geklammerten Ziffern des folgenden Gesetzestexts verweisen auf den entwurfssprachlichen Algorithmus. Reservezuweisungen sind unterstrichen:

"Aus dem Reingewinn ist jährlich ein Betrag von einem Zwanzigstel einem allgemeinen Reservefonds zuzuweisen (1), bis dieser Fonds die Höhe von einem Fünftel des einbezahlten Grundkapitals erreicht hat (2).

Diesem Reservefonds sind, auch nachdem er die gesetzliche Höhe erreicht hat, zuzuweisen: ...
ein Zehntel derjenigen Beträge (3), die aus dem Reingewinn nach der ordentlichen Speisung des Reservefonds (1) und nach Bezahlung einer Dividende von fünf vom Hundert an Aktionäre (4) und sonstige Gewinnbeteiligte (5) verteilt werden."

Die folgende Regelung ergänzt OR 671. Sie ist nicht bindend, in der Praxis aber weit verbreitet:

Nach der ordentlichen Reservezuweisung (s. 1) erhält der Verwaltungsrat eine Tantième in % des Gewinns (5). Nach der zweiten Speisung der Reserve (s. 3) sind vom Gewinnrest soviele ganze Prozent vom einbezahlten Grundkapital wie möglich als Zusatzdividende auszuschütten (6).

Eine verbale Spezifikation ist selten eindeutig. Die Interpretation der oben beschriebenen Erfolgsverteilung ist vor allem aus den folgenden Gründen schwierig:

b. Übung der Praxis

Die Praxis schüttet vom Gewinnrest meist soviele ganze Prozent Zusatzdividende vom Aktienkapital wie möglich aus. Eine Zusatzdividende erfordert eine zusätzliche Reservezuweisung von 10% (Reservezuweisung = 0.1*Zusatzdividende). Wir berechnen die ganzzahlige prozentuale Zusatzdividende schrittweise:

  1. Zunächst berechnen wir den Betrag, den wir als Zusatzdividende aus dem Saldo von 'Gewinnverteilung' (Gewinnrest) ausschütten könnten

  2. Dann drücken wir diesen Betrag als genauen Prozentsatz vom Aktienkapital aus.

  3. Diesen genauen Prozentsatz runden wir auf den ganzzahligen ab

  4. Schliesslich berechnen wir mit dem ganzzahligen Prozentsatz die endgültige Zusatzdividende

Der genaue Zusatzdividendensatz ZusatzdivSatz ergibt sich aus den Gleichungen (1) bis (3) und den algebraischen Vereinfachungen (4) bis (7): 

Zusatzdividende = ZusatzdivSatz * 0.01AK 					(1)
ZusatzdivSatz = (GV - ResAufZusatzdiv) / 0.01AK 				(2)
ResAufZusatzdiv = 0.1 * ZusatzdivSatz * 0.01AK 				(3)

(3) ist die Reservevorschrift von OR 671. (2) berechnet den ZusatzdivSatz, der sich nach Abzug der Reservezuweisung ResAufZusatzdiv noch aus dem Gewinnrest ausschütten lässt. Gleichung (4) vereinfacht (2), indem sie ResAufZusatzdiv durch (3) ersetzt. Nach den Schritten (5) und (6) ergibt (7) schliesslich den ZusatzdivSatz als Quotienten zwischen dem Gewinnrest GV und dem Produkts 0.011AK:

ZusatzdivSatz = (GV - 0.1 * ZusatzdivSatz * 0.01AK) / 0.01 AK		(4)
ZusatzdivSatz = GV / 0.01 AK - 0.1 * ZusatzdivSatz			(5)
1.1 ZusatzdivSatz = GV / 0.01AK						(6)
ZusatzdivSatz = GV / 0.01AK / 1.1 = GV / 0.011AK				(7)

Die VBA-Funktion Int ergibt den ganzzahligen Anteil ihres Arguments. Int(99.8) ergibt zum Beispiel die Integer-Zahl 99. Den ganzzahligen Zusatzdividendensatz berechnen wir deshalb als Int(GV / 0.011AK). Nach (1) lautet die VBA-Formel für die Zusatzdividende deshalb:

Zusatzdividende = Int(GV / 0.011AK) * 0.01AK


Entwurf

Die Arbeitsmappe Gewinnverteilung.xls setzt sich aus den folgenden Objekten zusammen:

Tabellenblatt Dialogblatt

Formular Eingabeformular

Modul Verarbeitung.

a) Kernalgorithmus

Bevor wir die Gewinnverteilung in VBA implementieren, formulieren wir eine erste entwurfssprachliche Annäherung an den Kernalgorithmus. Dabei verwenden wir die folgenden Abkürzungen:

ER     Erfolgsrechnung
GV     Gewinnverteilung
AK     Aktienkapital
T%     Tantième in %
kursiv Buchungssätze
Falls Aufwand < Ertrag dann                          '---- Gewinn
  Gewinn = Ertrag - Aufwand
  ER an GV, Gewinn
  Falls Reserve < AK / 5 dann                             '(2)
    GV an Reserve, Gewinn / 20                            '(1)
    GV an Dividende, Aktienkapital / 20                   '(4)
    GV an Tantième, Gewinn * T% / 100                     '(5)
    GV an Reserve, Gewinn * T% / 100 / 10                 '(3)
    ZusatzDivSatz =  GV / 0.011AK                         'siehe oben (3 c)
    Zusatzdividende = AK * Int (ZusatzDivSatz) / 100
    GV an Dividende,  Zusatzdividende                     '(5,6)
    GV an Reserve, Zusatzdividende / 10                   '(3)
  Falls GV > 0 dann
    GV an Reserve, GV
sonst                                                '---- Verlust
  Verlust = Aufwand - Ertrag
  Falls Verlust < Reserve dann
    Reserve an ER, Verlust
  sonst
    Reserve an ER, Reserve
    GV an ER, Verlust - Reserve
Gewinnverteilung gemäss OR 671 und Praxis

Wer diesen Algorithmus mit der Subroutine Buchungssätze()der Arbeitsmappe Gewinnverteilung.xls vergleicht, stösst auf den folgenden Unterschied: Die programmiersprachliche Version prüft für jeden Buchungssatz, ob der laufende Saldo des Gewinnverteilungskontos für die Buchung ausreicht. Um das Verständnis zu erleichtern haben wir diese Prüfung weggelassen und nur nur den algorithmischen Kern skizziert.

b) Dialogblatt

Der grösste Teil von Gewinnverteilung.xls widmet sich der Benutzeroberfläche. Das folgende Bild zeigt im Hintergrund die vier Spalten des Tabellenblatts Dialogblatt. Die Ausgabeanweisung Range(Zelle).Value = Wert schreibt jeden vom Programm ermittelten Wert in die passende Zelle von Dialogblatt. Ein Klick auf die Schaltflläche ‘Start’ ruft die Ereignisprozedur Start_Click() auf, die ihrerseits das Eingabeformular 'Falldaten eingeben' anzeigt.

Auf dem farbigen Hintergrund des Tabellenblatts sehen Sie ...

Eingabe- und Ausgabebereich von Gewinnverteilung.xls

Wie ein Tabellenblatt stellt auch ein benutzerdefiniertes Formular Objekte, Eigenschaften und Methoden bereit. Das obige Eingabeformular 'Falldaten eingeben' enthält für jede Eingabe ein Bezeichnungsfeld und ein Textfeld (weisses Feld rechts vom Bezeichnungsfeld).

c) Eingabeformular

Das nächste Bild zeigt den Entwurf des oben gezeigten Eingabeformulars. Der Entwurf verläuft in vier Schritten:

1. Entwicklungsumgebung aufrufen (»Alt/F11)
2. Leeren Formularrahmen zeichnen
3. Steuerlemente in den Formularrahmen setzen (Bezeichnungs- und Textfelder; Schaltflächen)
4. Den Steuerelementen Ereignisprozeduren zuordnen (abbrechen_Click, verbuchen_Click).

Entwurf des Eingabeformulars "Falldaten eingeben"

Das fertige Formular lässt sich mit mit dem VBA-Befehl Show anzeigen und mit Hide vorübergehend ausblenden.

Implementation in VBA

a) Modul Verarbeitung

Nach dem Entwurf des algorithmischen Kerns und der Benutzeroberfläche skizzieren wir den VBA-Code. Elemente des Objektmodells von Excel sind blau, 'Schaltflächen' stehen zwischen Hochkommas und Ereignisprozeduren sind rot. Anders als der Programmcode kontrolliert die entwurfssprachliche Variante von Buchungssätze() nicht für jeden Buchungssatz, ob der Saldo des Kontos Gewinnverteilung noch ausreicht.

Ereignisprozedur Start_Click()
  Ausgabebereich des Tabellenblatts Dialogblatt löschen
  Eingabeformular mit ‘Abbrechen’ und ‘Verbuchen’ anzeigen
  Falls abgebrochen <> True dann Buchungssätze()
Ereignisprozedur abbrechen_Click()
  Eingabeformular nach Klick auf ‘Abbrechen’ schliessen
  abgebrochen = True
Ereignisprozedur verbuchen_Click()
  '--- Leere und nichtnummerische Eingaben verhindern
  Für jedes Feld von Eingabeformular
    Falls Feld leer oder nichtnummerisch dann
      Formular nach Klick auf ‘Verbuchen’ nicht schliessen
      Fehlermeldung zeigen
  Kontrolle an den Benutzer zurückgeben
Subroutine Buchungssätze()
  Eingabeformular lesen (Aufwand, Ertrag, Aktienkapital, Reserve, Tantième in %)
  '-- Buchungssätze gemäss OR 671 und Praxis auf Dialogblatt schreiben
  Falls Gewinn dann
    BS "ER", "GV", Gewinn 'Reingewinn
    Falls Reserve < AK dann
      BS "GV", "Reserve", Gewinn / 20
    BS "GV", "Dividende", Aktienkapital / 20
    BS "GV", "Reserve", Gewinn / 20 / 10
    BS "GV", "Tantieme", Gewinn * Tantiemensatz / 100
    BS "GV", "Reserve", Gewinn * Tantiemensatz / 100 / 10
    BS "GV", "Dividende", AK * Int(GV / 0.011AK) / 100
    BS "GV", "Reserve", AK * Int(GV / 0.011AK) / 100 / 10
    Falls GV < 0 dann
      "GV", "Reserve", GV
  sonst ‘Verlust
    Falls Verlust < Reserve DANN
      BS "Reserve", "ER", Verlust
    sonst
      BS "Reserve", "ER", Reserve
      BS "GV", "ER", Verlust - Reserve
Subroutine BS(Sollkonto, Habenkonto, Buchungsbetrag)
  Buchungssatz auf Dialogblatt schreiben
  Konto Gewinnverteilung nachführen
Modul Verarbeitung (Objekt, Ereignisprozedur, ‘Schaltfläche’)

b) Codierung

Wir untersuchen den Programmcode nur ausschnittsweise:

  1. In einem ersten Schritt unterscheiden wir zwischen Variablen, die für das ganze Projekt, für ein Modul oder nur eine Prozedur gelten.

  2. Dann analysieren wir die Ereignisprozeduren der Start-Schaltfläche und des Eingabeformulars.

  3. Schliesslich gewinnen wir einen Überblick über das ganze Programmierprojekt Gewinnverteilung und lernen den den Objektkatalog kennen.

a. Globale und lokale Variablen

Der folgende Code-Abschnitt deklariert zu Beginn die Variable abgebrochen als öffentlich (Public), damit sie im ganzen Projekt, das heisst sowohl im Modul Dialogblatt als auch im Modul Eingabeformular, verwendet werden kann. Ausgabezeile und GV sind hingegen Private-Variablen, weil sie je nur in einem einzigen Modul vorkommen.

' --- Public für alle Module sichtbar
Public abgebrochen As Boolean   ' True, falls “Abbrechen”

' --- Private nur für das Verarbeitungsmodul sichtbar
Private Ausgabezeile As Integer
Private GV As Currency          ' Konto Gewinnverteilung

Eine Variable, die nur in jenem Modul oder in jener Prozedur sichtbar ist, in der sie vereinbart worden ist, heisst lokal. Der Programmierer darf zum Beispiel eine private Variable nur in jenem Modul verwenden, in dem er sie eingeführt hat. Wenn er hingegen eine Variable als Public vereinbart, dann darf er sie auch ausserhalb des deklarierenden Moduls, das heisst im ganzen Projekt, ansprechen. Variablen, die auch ausserhalb des deklarierenden Unterprogramms oder Moduls gelten, heissen global.

Einfachprogramme enthalten allerdings die Schlüsselwörter Public und Private nur selten. Am häufigsten ist das Schlüsselwort Dim. Es dient der Vereinbarung einer Variablen innerhalb einer einzigen Prozedur. Man sagt auch, der Gültigkeitsbereich einer Dim-Variablen beschränke sich auf die Prozedur. Private deklariert hingegen Variablen, die für alle Prozeduren eines Moduls gelten; solche Variablen sind also global innerhalb eines Moduls. Public macht sogar eine Vereinbarung für alle Module eines Projekts - zum Beispiel aller Module des Projekts “Gewinnverteilung” - sichtbar. Solche Variablen sind innerhalb eines Projekts global.

b. Ereignisprozeduren
Sub Start_Click()
  ' --- Dialogblatt und Eingabeformular initialisieren
  GV = 0
  abgebrochen = False
  ' Ausgabebereich des Dialogblatts löschen
  Dialogblatt.Range("A4:F11").ClearContents
  ' Eingabeformular die Kontrolle übergeben
  Eingabeformular.Show          ' laden und anzeigen
  ' --- Gefülltes Eingabeformular verarbeiten
  If abgebrochen = False Then
    Buchungssätze
    Unload Eingabeformular      ' aus Speicher entfernen
  End If
End Sub

Die Ereignisprozedur Start_Click() lädt mit dem Befehl Eingabeformular.Show das Eingabeformular in den Internspeicher und zeigt es auf dem Bildschirm. Die Zeile Unload Eingabeformular entfernt das Formular erst, nachdem der Benutzer die Dateneingabe regulär abgeschlossen hat oder die Schaltfläche ‘Abbrechen’ geklickt hat. Das Programm erkennt am Wert True der Public-Variable abgebrochen, dass der Benutzer die Eingabe abgebrochen hat. Anders als Unload Eingabeformular löscht Eingabeformular.Hide das Eingabeformular nicht aus dem Internspeicher, sondern zeigt es nur bis zum Befehl Eingabeformular.Show nicht mehr an.

Der nächste Programmausschnitt enthält die Ereignisprozeduren der beiden Schaltflächen ‘Abbrechen’ und ‘Verbuchen’. Verbuchen_Click() prüft nur die Gültigkeit der Eingabedaten, die Kernprozedur Buchungssätze wird aus Start_Click() aufgerufen.

'Modul Eingabeformular
'---------------------
Private Sub Abbrechen_Click()
  abgebrochen = True
  Unload Eingabeformular
End Sub

Private Sub Verbuchen_Click()
  'Eingabeformular nach Klick auf "Verbuchen" prüfen
  'Aufwand, etc. sind Textfelder des Eingabeformulars
  'Aufwand="" bedeutet dasselbe wie Aufwand.Value=""
  If Aufwand = "" Or _
    Ertrag = "" Or _
    Aktienkapital = "" Or _
    Reserve = "" Or _
    TantièmeInProzent = ""
  Then
    MsgBox "Keine Leereingaben möglich"
  ElseIf Not (IsNumeric(Aufwand) And _
    IsNumeric(Ertrag) And _
    IsNumeric(Aktienkapital) And _
    IsNumeric(Reserve) And _
    IsNumeric(TantièmeInProzent))
  Then
    MsgBox "Nur nummerische Eingaben möglich"
  Else
    Eingabeformular.Hide     'vorübergehend ausblenden
  End If
End Sub

In einem grösseren Projekt wie Gewinnverteilung helfen der Projektexplorer und der Objektkatalog, die Übersicht zu bewahren.


Aufgabe Übung Gewinnverteilung